package com.lambkit.plugin.groovy;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author yangyong(孤竹行)
 */
public class GroovyManager {

    private final ConcurrentHashMap<String, GroovyObject> groovyMap = new ConcurrentHashMap();

    private final ReentrantLock lock = new ReentrantLock();

    public Object invoke(String scriptText, String functionName, Map<String, Object> context) throws Exception {
        String scriptId = createScriptId(scriptText);
        return invoke(scriptId, scriptText, functionName, new Object[]{context});
    }

    private String createScriptId(String scriptText) {
        return SecureUtil.md5(scriptText);
        //return scriptText;
    }

    public Object invoke(String scriptId, String scriptData, String function, Object... object)    {
        GroovyObject scriptInstance = groovyMap.get(scriptId);
        if (scriptInstance == null) {
            lock.lock();
            try {
                scriptInstance = groovyMap.get(scriptId);
                if (scriptInstance == null ) {
                    if(StrUtil.isNotBlank(scriptData)) {
                        GroovyClassLoader loader = new GroovyClassLoader();
                        Class scriptClass = loader.parseClass(scriptData);
                        scriptInstance = (GroovyObject) scriptClass.getDeclaredConstructor().newInstance();
                        groovyMap.put(scriptId, scriptInstance);
                    }
                }
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
        }
        Object result = scriptInstance != null ? scriptInstance.invokeMethod(function, object) : null;
        return result;
    }
}
